﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using XFControls.XFPanels.XFPanelItems;

#pragma warning disable 1591
#pragma warning disable 219
#pragma warning disable 168

// ReSharper disable InconsistentNaming
// ReSharper disable UnaccessedField.Local
// ReSharper disable EnumUnderlyingTypeIsInt
// ReSharper disable PossibleNullReferenceException
// ReSharper disable BitwiseOperatorOnEnumWihtoutFlags
// ReSharper disable EmptyGeneralCatchClause
// ReSharper disable RedundantAssignment

namespace XFControls.Utils
{
    public static class ControlExtensions
    {
        public static void InvokeEx<TControl, TResult>(this TControl control, Func<TControl, TResult> func) where TControl : Control
        {
            if (control.IsDisposed) return;
            try
            {
                var ret = control.InvokeRequired ? (TResult)control.Invoke(func, control) : func(control);
            }
            catch (ObjectDisposedException) { }

            //return ret;
        }

        public static void InvokeEx<TControl>(this TControl control, Action<TControl> func) where TControl : Control
        {
            control.InvokeEx(c => { func(c); return c; });
        }

        public static void InvokeEx<TControl>(this TControl control, Action action) where TControl : Control
        {
            control.InvokeEx(c => action());
        }
    }

    public static class XFGraphicExtensions
    {
        #region RoundedCorners enum

        public enum RoundedCorners
        {
            None = 0x00,
            TopLeft = 0x02,
            TopRight = 0x04,
            BottomLeft = 0x08,
            BottomRight = 0x10,
            All = 0x1F
        }

        #endregion

        private const int DT_CALCRECT = 0x00000400;
        private const int DT_WORDBREAK = 0x00000010;

        [DllImport("coredll.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("coredll.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

        [DllImport("coredll.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("coredll.dll")]
        public static extern bool DeleteDC(IntPtr hdc);

        [DllImport("coredll.dll")]
        private static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref Rect lpRect, int wFormat);

        public static Size MeasureString(this Font f, string text, Rectangle rect)
        {
            Size result;
            IntPtr controlFont = f.ToHfont();
            IntPtr hDC = GetDC(IntPtr.Zero);
            using (Graphics gr = Graphics.FromHdc(hDC))
            {
                IntPtr originalObject = SelectObject(hDC, controlFont);
                result = MeasureString(gr, text, rect);
                SelectObject(hDC, originalObject); //Release resources    
            }
            DeleteDC(hDC);
            DeleteDC(controlFont);
            return result;
        }

        public static Size MeasureString(this Graphics gr, string text, Rectangle rect)
        {
            if (string.IsNullOrEmpty(text))
                return new Size();

            var bounds = new Rect(rect);
            IntPtr hdc = gr.GetHdc();
            int flags = DT_CALCRECT | DT_WORDBREAK;

            DrawText(hdc, text, text.Length, ref bounds, flags);
            gr.ReleaseHdc(hdc);

            return new Size(bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
        }

        public static void DrawRoundedBorder(this Graphics g, Color color, Rectangle rec,
                                             int radius, int borderWidth, RoundedCorners corners)
        {
            using (Bitmap b = XFBitmapFactory.GetBitmap(rec.Width, rec.Height))
            using (Graphics gb = Graphics.FromImage(b))
            {
                var gfRec = new Rectangle(0, 0, rec.Width, rec.Height);
                gb.Clear(Color.Green);

                gb.DrawRoundedRectangle(color, gfRec, radius, corners);

                gfRec.Height -= borderWidth << 1;
                gfRec.Width -= borderWidth << 1;
                gfRec.X += borderWidth;
                gfRec.Y += borderWidth;
                gb.DrawRoundedRectangle(Color.Green, gfRec, radius - borderWidth, corners);

                var maskAttr = new ImageAttributes();
                maskAttr.SetColorKey(Color.Green, Color.Green);

                g.DrawImage(b, rec, 0, 0, b.Width, b.Height, GraphicsUnit.Pixel, maskAttr);
            }
        }

        public static void DrawRoundedRectangle(this Graphics g, Color color, Rectangle rec, int radius,
                                                RoundedCorners corners)
        {
            using (var b = new SolidBrush(color))
            {
                int x = rec.X;
                int y = rec.Y;
                int diameter = radius * 2;
                var horiz = new Rectangle(x, y + radius, rec.Width, rec.Height - diameter);
                var vert = new Rectangle(x + radius, y, rec.Width - diameter, rec.Height);

                g.FillRectangle(b, horiz);
                g.FillRectangle(b, vert);

                if ((corners & RoundedCorners.TopLeft) == RoundedCorners.TopLeft)
                    g.FillEllipse(b, x, y, diameter, diameter);
                else
                    g.FillRectangle(b, x, y, diameter, diameter);

                if ((corners & RoundedCorners.TopRight) == RoundedCorners.TopRight)
                    g.FillEllipse(b, x + rec.Width - (diameter + 1), y, diameter, diameter);
                else
                    g.FillRectangle(b, x + rec.Width - (diameter + 1), y, diameter, diameter);

                if ((corners & RoundedCorners.BottomLeft) == RoundedCorners.BottomLeft)
                    g.FillEllipse(b, x, y + rec.Height - (diameter + 1), diameter, diameter);
                else
                    g.FillRectangle(b, x, y + rec.Height - (diameter + 1), diameter, diameter);

                if ((corners & RoundedCorners.BottomRight) == RoundedCorners.BottomRight)
                    g.FillEllipse(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter, diameter);
                else
                    g.FillRectangle(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter,
                                    diameter);
            }
        }

        public static void DrawRoundedRectangle(this Graphics g, Color color, Rectangle rec, int radius)
        {
            DrawRoundedRectangle(g, color, rec, radius, RoundedCorners.All);
        }

        public static void DrawRoundedRectangleBorder(this Graphics g, Color color, Color bColor, Rectangle rec,
                                                      int radius, int borderWidth, RoundedCorners corners)
        {
            g.DrawRoundedRectangle(bColor, rec, radius, corners);

            if (borderWidth == 0)
                return;

            rec.Height -= borderWidth * 2;
            rec.Width -= borderWidth * 2;
            rec.X += borderWidth;
            rec.Y += borderWidth;
            g.DrawRoundedRectangle(color, rec, radius - borderWidth, corners);
        }

        public static void DrawRoundedRectangleBorder(this Graphics g, Color color, Color bColor, Rectangle rec,
                                                      int radius, int borderWidth)
        {
            DrawRoundedRectangleBorder(g, color, bColor, rec, radius, borderWidth, RoundedCorners.All);
        }

        public static void DrawRoundedRectangleGradiantFill(this Graphics g, Color start, Color end, Rectangle rec,
                                                            int radius, RoundedCorners corners)
        {
            DrawRoundedRectangleBorderGradiantFill(g, start, end, Color.White, rec, radius, 0, corners);
        }

        public static void DrawRoundedRectangleGradiantFill(this Graphics g, Color start, Color end, Rectangle rec,
                                                            int radius)
        {
            DrawRoundedRectangleBorderGradiantFill(g, start, end, Color.White, rec, radius, 0);
        }

        public static void DrawRoundedRectangleBorderGradiantFill(this Graphics g, Color start, Color end, Color bColor,
                                                                  Rectangle rec, int radius, int borderWidth,
                                                                  RoundedCorners corners)
        {
            using (var b = XFBitmapFactory.GetBitmap(rec.Width, rec.Height))
            using (Graphics gGf = Graphics.FromImage(b))
            using (var bMask = XFBitmapFactory.GetBitmap(rec.Width, rec.Height))
            using (Graphics gMask = Graphics.FromImage(bMask))
            {
                var gfRec = new Rectangle(0, 0, rec.Width, rec.Height);
                gGf.GradientFill(gfRec, start, end, GradientFillDirection.Vertical);

                gMask.Clear(Color.White);
                gMask.DrawRoundedRectangleBorder(Color.Green, bColor, gfRec, radius, borderWidth, corners);

                var maskAttr = new ImageAttributes();
                maskAttr.SetColorKey(Color.Green, Color.Green);
                gGf.DrawImage(bMask, gfRec, 0, 0, gfRec.Width, gfRec.Height, GraphicsUnit.Pixel, maskAttr);

                g.DrawAlphaFirstPix(b, rec, gfRec);
            }
        }

        public static void DrawRoundedRectangleBorderGradiantFill(this Graphics g, Color start, Color end, Color bColor,
                                                                  Rectangle rec, int radius, int borderWidth)
        {
            DrawRoundedRectangleBorderGradiantFill(g, start, end, bColor, rec, radius, borderWidth, RoundedCorners.All);
        }

        public static void DrawRoundedRectangleBorderImageFill(this Graphics g, object sender, string Uri, Color bColor,
                                                               Rectangle rec, int radius, int borderWidth,
                                                               RoundedCorners corners)
        {
            using (var b = XFBitmapFactory.GetBitmap(rec.Width, rec.Height))
            using (Graphics gGf = Graphics.FromImage(b))
            using (var bMask = XFBitmapFactory.GetBitmap(rec.Width, rec.Height))
            using (Graphics gMask = Graphics.FromImage(bMask))
            {
                gGf.Clear(Color.White);
                var gfRec = new Rectangle(0, 0, rec.Width, rec.Height);

                gGf.DrawUIAlphaChannel(sender, Uri, gfRec);
                gMask.Clear(Color.White);
                gMask.DrawRoundedRectangleBorder(Color.Green, bColor, gfRec, radius, borderWidth, corners);

                var maskAttr = new ImageAttributes();
                maskAttr.SetColorKey(Color.Green, Color.Green);
                gGf.DrawImage(bMask, gfRec, 0, 0, gfRec.Width, gfRec.Height, GraphicsUnit.Pixel, maskAttr);

                g.DrawAlphaFirstPix(b, rec, gfRec);
            }
        }

        public static void DrawFileIcon(this Graphics g, string filePath, Rectangle rectangle)
        {
            var shinfo = new SHFILEINFO();

            Win32.SHGetFileInfo(filePath, 0, ref shinfo,
                                (uint)Marshal.SizeOf(shinfo),
                                Win32.SHGFI_ICON |
                                Win32.SHGFI_SMALLICON);

            Icon myIcon = Icon.FromHandle(shinfo.hIcon);

            g.DrawIcon(myIcon, rectangle.X, rectangle.Y);
        }

        public static int DrawString(this Graphics g, string text, Rectangle rectangle, XFItemStyle style)
        {
            return DrawString(g, text, rectangle, style, style.TextColor);
        }

        public static int DrawString(this Graphics g, string text, Rectangle rectangle, XFItemStyle style, Color c)
        {
            return DrawString(g, text, rectangle, style, style.TextFont, c);
        }

        public static int DrawString(this Graphics g, string text, Rectangle rectangle, XFItemStyle style, Font f,
                                     Color c)
        {
            return DrawString(g, text, rectangle, style.TextAlign, f, c);
        }

        public static int DrawString(this Graphics g, string text, Rectangle rectangle, XFItemTextAlign align, Font f,
                                     Color c)
        {
            if (string.IsNullOrEmpty(text)) return 0;

            using (var b = new SolidBrush(c))
            {
                Size size = new Size();

                if (rectangle.Height == 0)
                    size = f.MeasureString(text, rectangle);

                switch (align)
                {
                    case XFItemTextAlign.Center:
                        rectangle.Height += size.Height;
                        g.DrawString(text, f, b, rectangle, new StringFormat { Alignment = StringAlignment.Center });
                        break;
                    case XFItemTextAlign.Justified:
                        g.DrawJustifiedString(rectangle.X, rectangle.Y, rectangle.Width, text, f, c, true);
                        return rectangle.Height;
                    case XFItemTextAlign.Left:
                        rectangle.Height += size.Height;
                        g.DrawString(text, f, b, rectangle);
                        break;
                    case XFItemTextAlign.Right:
                        rectangle.Height += size.Height;
                        g.DrawString(text, f, b, rectangle, new StringFormat { Alignment = StringAlignment.Far });
                        break;
                }

                return size.Height;
            }
        }

        #region Nested type: Rect

        public struct Rect
        {
            public int Left, Top, Right, Bottom;
            public Rect(Rectangle r)
            {
                Left = r.Left;
                Top = r.Top;
                Bottom = r.Bottom;
                Right = r.Right;
            }
        }
        #endregion
    }
}